package com.jiaozi.base.redis;

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.Set;
/**
 * Redis工具类
 */
@Slf4j
@Component
@Data
@NoArgsConstructor
public class RedisUtil {
    @Value("${spring.data.redis.host}")
    private String host = "";
    @Value("${spring.data.redis.port}")
    private int port = 0;
    private int expire = 0;
    @Value("${spring.data.redis.timeout}")
    private int timeout = 0;
    @Value("${spring.data.redis.password}")
    private String password = "";

    private static JedisPool jedisPool = null;
    /**
     * 减车位数Lua逻辑：首先判断车位数是否存在，以及车位数余量是否够本次数量，如果不够，则返回0，如果够则完成扣减并返回1
     * -   KEYS[1]：车位数的key
     * -   KEYS[2]：车位数的扣减数量
     */
    private String STORE_DECR_SCRIPT_LUA = "\n" +
            "local c_s = redis.call('get', KEYS[1])\n" +
            "if not c_s or tonumber(c_s) < tonumber(KEYS[2]) then\n" +
            "   return 0\n" +
            "end\n" +
            "redis.call('decrby',KEYS[1], KEYS[2])";

    /**
     * 增加车位数Lua逻辑：首先判断车位数是否存在，如果存在，则补充库存
     * -   KEYS[1]：车位数的key
     * -   KEYS[2]：车位数的补充数量
     */
    private String STORE_INCR_SCRIPT_LUA = "\n" +
            "local c_s = redis.call('get', KEYS[1])\n" +
            "if not c_s then\n" +
            "   return 0\n" +
            "end\n" +
            "redis.call('incrby',KEYS[1], KEYS[2])";


    /**
     * 在系统启动时，将脚本预加载到Redis中，并返回一个加密的字符串，下次只要传该加密串，即可执行对应的脚本，减少了Redis的预编译
     * 减车位数sha
     */
    private String STORE_DECR_SCRIPT_SHA = "";

    /**
     * 增加车位数sha
     */
    private String STORE_INCR_SCRIPT_SHA = "";

    /**
     * 初始化
     *
     * @return
     */
    @Bean
    public JedisPool initJedisPool() {
        log.info(host);

        if (jedisPool == null) {
            if (password != null && !"".equals(password)) {
                jedisPool = new JedisPool(new JedisPoolConfig(), host, port, timeout, password);
            } else if (timeout != 0) {
                jedisPool = new JedisPool(new JedisPoolConfig(), host, port, timeout);
            } else {
                jedisPool = new JedisPool(new JedisPoolConfig(), host, port);
            }
        }
        Jedis jedis = jedisPool.getResource();
        STORE_DECR_SCRIPT_SHA = jedis.scriptLoad(STORE_DECR_SCRIPT_LUA);
        STORE_INCR_SCRIPT_SHA = jedis.scriptLoad(STORE_INCR_SCRIPT_LUA);

        return jedisPool;
    }

    /**
     * 获取jedis对象，并指定dbIndex
     *
     * @param dbIndex
     */
    public Jedis getJedis(Integer dbIndex) {
        Jedis jedis = jedisPool.getResource();
        if (dbIndex == null || dbIndex > 15 || dbIndex < 0) {
            dbIndex = 0;
        }
        jedis.select(dbIndex);
        return jedis;
    }


    /**
     * 减数
     *
     * @param key     key
     * @param num     要车位数存数量
     * @param dbIndex 数据库索引
     * @return 0失败  1成功
     */
    public Long decrStorage(String key, String num, int dbIndex) {
        try (Jedis jedis = getJedis(dbIndex)) {
            Object result = jedis.evalsha(STORE_DECR_SCRIPT_SHA, 2, key, num);
            return (Long) result;
        }
    }

    /**
     * 补数
     *
     * @param key     车位数key
     * @param num     要车位数存数量
     * @param dbIndex 数据库索引
     * @return 0失败  1成功
     */
    public Long incrStorage(String key, String num, int dbIndex) {
        try (Jedis jedis = getJedis(dbIndex)) {
            Object result = jedis.evalsha(STORE_INCR_SCRIPT_SHA, 2, key, num);
            return (Long) result;
        }
    }


    /**
     * 嫌少
     *
     * @param redisEnum
     * @param key
     * @param num
     * @return
     */
    public Long decrStorage(RedisEnum redisEnum, String key, String num) {
        return decrStorage(redisEnum.getKey() + key, num, redisEnum.getDbIndex());
    }

    /**
     * 增加
     *
     * @param redisEnum
     * @param key
     * @param num
     * @return
     */
    public Long incrStorage(RedisEnum redisEnum, String key, String num) {
        return incrStorage(redisEnum.getKey() + key, num, redisEnum.getDbIndex());
    }


    /**
     * key是否存在
     *
     * @param key
     * @param dbIndex
     */
    public Boolean existsKey(String key, int dbIndex) {
        try (Jedis jedis = getJedis(dbIndex)) {
            return jedis.exists(key);
        }
    }


    /**
     * 添加缓存
     *
     * @param key     键
     * @param value   值
     * @param dbIndex 库索引
     * @return
     */
    public String setString(String key, String value, int dbIndex) {
        String String;
        try (Jedis jedis = getJedis(dbIndex)) {
            String = jedis.set(key, value);
        }
        return String;
    }

    /**
     * 超时时间
     */
    public void setStringExpire(String key, String value, long expire, int dbIndex) {
        try (Jedis jedis = getJedis(dbIndex)) {
            if (expire != 0) {
                jedis.set(key, value);
                jedis.expire(key, expire);
            }
        }
    }


    public void removeString(String key, int dbIndex) {
        try (Jedis jedis = getJedis(dbIndex)) {
            jedis.del(key);
        }
    }

    public Long removeBatch(String key, int dbIndex) {
        Long l = 0L;
        try (Jedis jedis = getJedis(dbIndex)) {

            for (String s : jedis.keys(key)) {
                l = l + jedis.unlink(s);
            }
        }
        return l;
    }


    public String getString(String key, int dbIndex) {
        Jedis jedis = getJedis(dbIndex);
        String value;
        try {
            value = jedis.get(key);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return value;
    }


    public Set<String> getKeyBatch(String key, int dbIndex) {
        Jedis jedis = getJedis(dbIndex);
        try {
            return jedis.keys(key);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    public Set<String> getKeyBatch(RedisEnum redisEnum) {
        Jedis jedis = getJedis(redisEnum.getDbIndex());
        try {

            return jedis.keys(redisEnum.getKey() + "*");
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }
    public Set<String> getKeyBatch(RedisEnum redisEnum,String key) {
        Jedis jedis = getJedis(redisEnum.getDbIndex());
        try {

            return jedis.keys(redisEnum.getKey() +key+ ":*");
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }


    public void removeBatchString(RedisEnum redisEnum) {
        removeBatch(redisEnum.getKey() + "*", redisEnum.getDbIndex());
    }

    public void removeBatchString(RedisEnum redisEnum, String key) {
        removeBatch(redisEnum.getKey() + key + "/*", redisEnum.getDbIndex());
    }


    public void removeString(RedisEnum redisEnum) {
        try (Jedis jedis = getJedis(redisEnum.getDbIndex())) {
            jedis.del(redisEnum.getKey());
        }
    }

    public void removeString(RedisEnum redisEnum, String key) {
        try (Jedis jedis = getJedis(redisEnum.getDbIndex())) {
            jedis.del(redisEnum.getKey() + key);
        }
    }

    public void setStringExpire(RedisEnum redisEnum, String value, long expire) {
        try (Jedis jedis = getJedis(redisEnum.getDbIndex())) {
            if (expire != 0) {
                jedis.set(redisEnum.getKey(), value);
                jedis.expire(redisEnum.getKey(), expire);
            }
        }
    }

    public void setStringExpire(RedisEnum redisEnum, String key, String value, long expire) {
        try (Jedis jedis = getJedis(redisEnum.getDbIndex())) {
            if (expire != 0) {
                jedis.set(redisEnum.getKey() + key, value);
                jedis.expire(redisEnum.getKey() + key, expire);
            }
        }
    }

    public String setString(RedisEnum redisEnum, String value) {
        String String;
        try (Jedis jedis = getJedis(redisEnum.getDbIndex())) {
            String = jedis.set(redisEnum.getKey(), value);
        }
        return String;
    }

    public String setString(RedisEnum redisEnum, String key, String value) {
        String String;
        try (Jedis jedis = getJedis(redisEnum.getDbIndex())) {
            String = jedis.set(redisEnum.getKey() + key, value);
        }
        return String;
    }

    public String getString(RedisEnum redisEnum) {
        Jedis jedis = getJedis(redisEnum.getDbIndex());
        String value;
        try {
            value = jedis.get(redisEnum.getKey());
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return value;
    }


    public String getString(RedisEnum redisEnum, String key) {
        Jedis jedis = getJedis(redisEnum.getDbIndex());
        String value;
        try {
            value = jedis.get(redisEnum.getKey() + key);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return value;
    }


    /**
     * key是否存在
     */
    public Boolean existsKey(RedisEnum redisEnum, String key) {
        try (Jedis jedis = getJedis(redisEnum.getDbIndex())) {
            return jedis.exists(redisEnum.getKey() + key);
        }
    }

    /**
     * key是否存在
     */
    public Boolean existsKey(RedisEnum redisEnum) {
        try (Jedis jedis = getJedis(redisEnum.getDbIndex())) {
            return jedis.exists(redisEnum.getKey());
        }
    }

    /**
     * key剩余时间
     */
    public Long ttl(String key, int dbIndex) {
        try (Jedis jedis = getJedis(dbIndex)) {
            return jedis.ttl(key);
        }
    }


    /**
     * key剩余时间
     */
    public Long ttl(RedisEnum redisEnum, String key) {
        try (Jedis jedis = getJedis(redisEnum.getDbIndex())) {
            return jedis.ttl(redisEnum.getKey() + key);
        }
    }

    /**
     * key剩余时间
     */
    public Long ttl(RedisEnum redisEnum) {
        try (Jedis jedis = getJedis(redisEnum.getDbIndex())) {
            return jedis.ttl(redisEnum.getKey());
        }
    }





}